#define _POSIX_SOURCE
#define _BSD_SOURCE

#include "dominion.h"
#include "dominion_helpers.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include "rngs.h"
#include <getopt.h>

#ifndef TEST_CASES
#define TEST_CASES 10000
#endif

#ifndef RAND_NUM
#define RAND_NUM 100
#endif

#ifndef SUPP
#define SUPP 10
#endif

int createGameState(struct gameState *state, int card, int *handPos)
{
	int i, j;

	// Generate 2-4 players
	state->numPlayers = floor(Random() * (MAX_PLAYERS - 1)) + 2;
	state->whoseTurn = floor(Random() * state->numPlayers);

	state->numActions = floor(Random() * RAND_NUM);
	state->numBuys = floor(Random() * RAND_NUM);
	state->coins = floor(Random() * RAND_NUM);
	state->playedCardCount = floor(Random() * RAND_NUM);

	// Generate Supply Cards
	for (i = 0; i <= treasure_map; i++) {
		state->supplyCount[i] = floor(Random() * SUPP);
	}

	for (i = 0; i < state->numPlayers; i++) {
		state->handCount[i] = floor(Random() * RAND_NUM);
		
		if (i == (state->whoseTurn + 1) % (state->numPlayers)) {
			state->deckCount[i] = floor(Random() * 3);
			state->discardCount[i] = floor(Random() * 2);
		} else {
			state->deckCount[i] = floor(Random() * (RAND_NUM - 4)) + 5;
			state->discardCount[i] = floor(Random() * RAND_NUM);
		}

		for (j = 0; j < state->handCount[i]; j++) {
			state->hand[i][j] = floor(Random() * (treasure_map + 1));
		}

		for (j = 0; j < state->deckCount[i]; j++) {
			state->deck[i][j] = floor(Random() * (treasure_map + 1));
		}

		for (j = 0; j < state->discardCount[i]; j++) {
			state->discard[i][j] = floor(Random() * (treasure_map + 1));
		}
	}

	// Make sure current player has the card to test
	if (state->handCount[state->whoseTurn] <= 0) {
		state->hand[state->whoseTurn][0] = card;
		state->handCount[state->whoseTurn] += 1;
		*handPos = 0;
	} else {
		*handPos = floor(Random() * state->handCount[state->whoseTurn]);
		state->hand[state->whoseTurn][*handPos] = card;
	}

	return 0;
}

int test_cardtribute(struct gameState *state, int handPos)
{
	int i, status = 0, nextPlayer, extraActions = 0, extraCoins = 0, extraHand = 0;
	int revealCards[2] = {-1, -1};
	struct gameState pre;

	memcpy(&pre, state, sizeof(struct gameState));

	nextPlayer = (state->whoseTurn + 1) % (state->numPlayers);

	if (state->deckCount[nextPlayer] + state->deckCount[nextPlayer] == 1) {
		if (state->deckCount[nextPlayer] == 1) {
			revealCards[0] = state->deck[nextPlayer][0];
		
		} else if (state->discardCount[nextPlayer] == 1) {
			revealCards[0] = state->discard[nextPlayer][0];
		}
	} else if (state->deckCount[nextPlayer] >= 2) {
		revealCards[0] = state->deck[nextPlayer][state->deckCount[nextPlayer] - 1];
		revealCards[1] = state->deck[nextPlayer][state->deckCount[nextPlayer] - 2];
	} else if (state->deckCount[nextPlayer] == 1 && state->discardCount[nextPlayer] >= 1) {
		revealCards[0] = state->deck[nextPlayer][0];
		revealCards[1] = state->discard[nextPlayer][0];
	} else {
		// Can't test situation due to shuffle randomness
		return 1;
	}

	if (revealCards[1] == revealCards[0]) {
		revealCards[1] = -1;
	}

	for (i = 0; i < 2; i++) {
		if (revealCards[i] == -1) {
			break;
		}

		if (revealCards[i] == copper || revealCards[i] == silver || revealCards[i] == gold) {
			extraCoins += 2;
		} else if (revealCards[i] == estate || revealCards[i] == duchy || revealCards[i] == province || revealCards[i] == gardens || revealCards[i] == great_hall) {
			if (state->deckCount[state->whoseTurn] + state->discardCount[state->whoseTurn] - extraHand >= 2) {
				extraHand += 2;
			} else if (state->deckCount[state->whoseTurn] + state->discardCount[state->whoseTurn] - extraHand == 1) {
				extraHand += 1;
			}
		} else {
			extraActions += 2;
		}
	}

	cardEffect(tribute, -1, -1, -1, state, handPos, NULL);

	if (pre.coins + extraCoins != state->coins) {
		fprintf(stderr, "\t# ERROR: Coin does not match (Pre: %d Post: %d)\n", pre.coins + extraCoins, state->coins);
		status = -1;
	}

	if (pre.numActions + extraActions != state->numActions) {
		fprintf(stderr, "\t# ERROR: Action does not match (Pre: %d Post: %d)\n", pre.numActions + extraActions, state->numActions);
		status = -1;
	}

	if (pre.handCount[pre.whoseTurn] + extraHand - 1 != state->handCount[state->whoseTurn] && state->playedCards[state->playedCardCount - 1] == tribute) {
		fprintf(stderr, "\t# ERROR: Hand does not match (Pre: %d Post: %d)\n", pre.deckCount[pre.whoseTurn] + extraHand,state->deckCount[state->whoseTurn]);
		status = -1;
	}

	return status;
}

int main(int argc, char **argv)
{
	int i, result, handPos, counter = 0;
	struct gameState state;

	for (i = 0; i < TEST_CASES; i++) {

		// Reset variables
		handPos = 0;
		
		createGameState(&state, tribute, &handPos);
		result = test_cardtribute(&state, handPos);
		if (result == 0) {
			printf("TEST CASE #%d = PASS\n", i + 1);
			counter += 1;
		} else if (result == 1) { // Case that cannot be tested
			i--;
			continue;
		} else {
			printf("TEST CASE #%d = FAIL <------\n", i + 1);
			//outputState(&state, stderr);
		}
	}

	printf("PASSED CASES: %d out of %d\n", counter, TEST_CASES);
	return 0;
}